home *** CD-ROM | disk | FTP | other *** search
- /*
- Copyright (c) 1986, 1990 by The Trustees of Columbia University in
- the City of New York. Permission is granted to any individual or
- institution to use, copy, or redistribute this software so long as it
- is not sold for profit, provided this copyright notice is retained.
-
- Author: Andrew Lowry
- */
- /* cmnum
- **
- ** This module contains the standard break table and handlers for
- ** parsing integers. Parsing succeeds if current input contains an
- ** integer in a given radix (up to 16), and the return value is the int
- ** value parsed. Optional flags can specify that a sign may or may not
- ** prefix the integer, and whether a successful parse should result if
- ** the integer is terminated by anything other than punctuation (as in
- ** "12am" as opposed to "12 am"). If parse-only is specified in the
- ** FDB (flag NUM_PO), the syntax of the number is checked, but no attempt
- ** at binary conversion is made, so overflow errors cannot occur. In this
- ** case, the return value is the (possibly signed) string representing
- ** the integer. It is left in the atom buffer.
- **
- ** Full completion simply terminates current input with a space if
- ** there is a valid integer. If the input is empty or consists only
- ** of a sign, full completion beeps. Partial completion always beeps.
- ** The standard break table allows digits, and letters A-F (and a-f).
- ** Minus and plus signs are also allowed in first position.
- **/
-
- #define NUMERR /* declare cmnum parse errors here */
-
- #include "ccmdlib.h" /* get ccmd package symbols */
- #include "cmfncs.h" /* and internal symbols */
-
- /* Forward declaration of handler routines */
-
- int numprs(), numhlp(), numcplt();
-
- static brktab numbrk = {
- { /* 1st char break array */
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xeb, 0x00, 0x3f, /* allow - and + */
- 0x81, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff
- },
- { /* subsequent char break array */
- 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x3f, /* disallow - and + */
- 0x81, 0xff, 0xff, 0xff, 0x81, 0xff, 0xff, 0xff
- }
- };
-
- ftspec ft_num = { numprs, numhlp, numcplt, 0, &numbrk }; /* handler structure */
-
-
-
- /* numprs - Locate the digit string in the current input. If there is
- ** none, error. If NUM_PO is on in the FDB, the integer is parsed and
- ** returned as a string, with no attempt at binary conversion. Otherwise
- ** convert to binary, keeping watch for overflow.
- **/
-
- PASSEDSTATIC int
- numprs(text,textlen,fdbp,parselen,value)
- char *text;
- int textlen,*parselen;
- fdb *fdbp;
- pval *value;
- {
- char *digits,*term,c; /* for decomposing the input */
- int sign;
- int ndig; /* number of digits */
- int radix; /* radix specified in the FDB */
- int flags; /* flags specified in the FDB */
- unsigned int valhi,vallo; /* high and low pieces of binary value */
- int dig; /* integer value of individual digit */
- int intbits; /* number of bits per integer */
- int ret; /* return code from aux routine */
-
- radix = (int) fdbp->_cmdat; /* get the radix */
- flags = fdbp->_cmffl; /* and parse flags */
- /* parse the input */
- ret = prsnum(text,textlen,flags,radix,&sign,&digits,&ndig,&term);
- if (ret != CMxOK)
- return(ret); /* propagate problems */
- if (term == NULL)
- return(CMxINC); /* unterminated field is incomplete */
- if (digits == NULL)
- return(NUMxNP); /* terminated but no digits */
- if (ndig < 1) /* check ndig too - chris */
- return (NUMxNP); /* terminated but no digits */
- if (flags & NUM_PO) { /* parse only? */
- value->_pvstr = cmcsb._cmabp; /* yup, string value in atom buffer */
- *parselen = term - text; /* compute number of characters */
- return(CMxOK);
- }
- valhi = -1; /* count bits */
- for (intbits = 0; valhi != 0; intbits++)
- valhi <<= 1; /* shift out bits until zeros remain */
-
- valhi = vallo = 0; /* start with zero, then add in digits */
- while (ndig-- > 0) { /* loop through all the digits */
- vallo *= radix; /* multiply both pieces by radix */
- valhi *= radix;
- dig = ((*digits++) & CC_CHR) - '0'; /* get value of next digit */
- if (dig > 9)
- dig -= ('A'-'0')-10; /* adjust non-numeric digits */
- if (dig > 15)
- dig -= 'a'-'A'; /* further adjust lowercase */
- vallo += dig; /* add in the new digit */
- valhi += vallo >> 4; /* leave only 4 bits in low part */
- vallo &= 0xf;
- if ((valhi >> (intbits-4)) != 0)
- return(NUMxOV); /* overflow */
- } /* go do next digit */
- if (flags & NUM_US) /* unsigned integer? */
- valhi = (valhi << 4) | vallo; /* shift all bits into high piece */
- else if (sign == -1) /* need to negate it? */
- if ((valhi >> intbits-5) == 0) /* in range for positive? */
- valhi = -((valhi << 4) | vallo); /* yup, make abs val and negate */
- else if (((valhi << 5) == 0) && (vallo == 0))
- valhi = 1 << (intbits-1); /* no, handle special case -2^(intbits-1) */
- else
- return(NUMxOV); /* overflow */
- else /* positive signed integer */
- if ((valhi >> intbits-5) == 0) /* in range? */
- valhi = (valhi << 4) | vallo; /* yup, put pieces together */
- else
- return(NUMxOV); /* nope, overflow */
-
- value->_pvint = (int) valhi; /* set value in place */
- *parselen = term - text; /* compute consumed region */
- return(CMxOK); /* and give a good parse */
- }
-
-
-
- /* numcplt - Partial completion always comes up empty. Full completion
- ** gets a space if there are any digits in the buffer.
- **/
-
- PASSEDSTATIC int
- numcplt(text,textlen,fdbp,full,cplt,cpltlen)
- char *text,**cplt;
- int textlen,full,*cpltlen;
- fdb *fdbp;
- {
- int radix,flags; /* radix and flags for parse */
- char *digits,*term; /* decomposed input */
- int sign,ndig;
- int ret; /* return code from aux routine */
-
- radix = (int) fdbp->_cmdat; /* get radix and flags */
- flags = fdbp->_cmffl;
- /* try to parse the data */
- ret = prsnum(text,textlen,flags,radix,&sign,&digits,&ndig,&term);
-
- *cplt = NULL; /* we never supply completion text */
- if (!full || (ndig == 0)) /* partial or no digits yet? */
- return(CMP_BEL); /* then just beep */
- else
- return(CMP_SPC | CMP_GO); /* full gets a space and wakeup */
- }
-
-
-
- /* numhlp - Just give a message customized by the requested radix.
- **/
-
- PASSEDSTATIC int
- numhlp(text,textlen,fdbp,cust,lines)
- char *text;
- int textlen,cust;
- fdb *fdbp;
- int lines;
- {
- int radix; /* requested radix and flags */
- int flags;
- int caseadj = 0; /* used when lowercasing initials */
- char raddigs[3]; /* space for decimal string for radix value */
-
- /* macro to print upper or lowercase char depending on value of caseadj */
- #define caseout(x) cmxputc((char) (x)+caseadj)
-
- radix = (int) fdbp->_cmdat; /* get flags and radix */
- flags = fdbp->_cmffl;
-
- if (flags & NUM_US) {
- cmxputs("Unsigned "); /* indicate if signs not allowed */
- caseadj = 'a'-'A'; /* set up for lowercase initials */
- }
- switch (radix) {
- case 2: caseout('B'); cmxputs("inary integer"); break;
- case 8: caseout('O'); cmxputs("ctal integer"); break;
- case 10: caseout('D'); cmxputs("ecimal integer"); break;
- case 16: caseout('H'); cmxputs("exadecimal integer"); break;
- default: caseout('I'); cmxputs("nteger in base ");
- if (radix >= 10) /* output radix in decimal */
- cmxputc('1');
- cmxputc((char) (radix % 10) + '0');
- break;
- }
- return(lines-1);
- }
-
-
-
- /* prsnum - Auxiliary routine for parse and completion.
- **
- ** Purpose:
- ** Breaks the current input into three pieces - sign, digits, and
- ** terminator, and checks to make sure that restrictions called
- ** for in the parse data flags are observed.
- **
- ** Input arguments:
- ** text - Pointer to input text.
- ** textlen - Number of characters of input text.
- ** flags - Flags supplied in the FDB.
- ** radix - Radix specified in the FDB.
- **
- ** Output arguments:
- ** sign - 0 for no sign, 1 for +, -1 for -.
- ** digits - Points to beginning of digit string.
- ** ndig - Number of digits in digit string.
- ** term - Points to terminating character.
- **
- ** Returns: Standard error code.
- **/
-
- static int
- prsnum(text,textlen,flags,radix,sign,digits,ndig,term)
- char *text, **digits, **term;
- int textlen, flags, radix, *sign, *ndig;
- {
- int dig; /* value of individual digit */
- char c;
-
- if ((radix < 2) || (radix > 16)) /* check for bad radix */
- return(NUMxRAD);
-
- *ndig = 0; /* no digits scanned yet */
- *sign = 0; /* assume no sign */
- *digits = text; /* so digits start right away */
- *term = NULL; /* and no terminator given */
-
- if (textlen == 0)
- return(CMxOK); /* no text - all assumptions true */
-
- c = *text & CC_CHR; /* pick up first char */
- if ((c == '+') || (c == '-')) { /* sign at front? */
- if (flags & NUM_US) /* yup, check unsigned flag */
- return(NUMxSGN); /* was not supposed to be there */
- *sign = (c == '+') ? 1 : -1; /* set the sign value */
- *digits = ++text; /* move digit start */
- textlen--; /* and count the character*/
- }
-
- while (textlen-- > 0) {
- c = *text & CC_CHR; /* get next digit */
- if (isdigit(c))
- dig = c - '0'; /* adjust numeric digits */
- else if (isupper(c))
- dig = (c - 'A') + 10; /* adjust uppercase digits */
- else if (islower(c))
- dig = (c - 'a') + 10; /* adjust lowercase digits */
- else
- dig = 16; /* anything else invalid */
- if (dig >= radix) /* terminator found? */
- if (!isalnum(c) || (flags & NUM_BNP)) { /* valid terminator? */
- *term = text; /* point at terminator */
- return(CMxOK); /* and return */
- }
- else
- return(NUMxNP); /* bad terminator */
- else {
- (*ndig)++; /* else bump digit counter */
- text++; /* and move on */
- }
- }
- return(CMxOK);
- }
-